package com.ruoyi.project.business.monthschedule.service.impl;

import com.alibaba.fastjson.JSON;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.project.business.askleave.service.IAskLeaveService;
import com.ruoyi.project.business.domain.entity.MonthScheduleEmp;
import com.ruoyi.project.business.domain.entity.ScheduleDefineMirror;
import com.ruoyi.project.business.domain.model.month.EmpDailySchedule;
import com.ruoyi.project.business.domain.model.month.MonthScheduleSolution;
import com.ruoyi.project.business.domain.model.month.ShiftDefine;
import com.ruoyi.project.business.domain.rules.Rule;
import com.ruoyi.project.business.domain.rules.month.EmpCountPerDay;
import com.ruoyi.project.business.domain.strategy.CommonStrategy;
import com.ruoyi.project.business.domain.strategy.PageClickAssignStrategy;
import com.ruoyi.project.business.domain.utils.RuleUtil;
import com.ruoyi.project.business.monthschedule.entity.MonthSchedule;
import com.ruoyi.project.business.monthschedule.mapper.MonthScheduleMapper;
import com.ruoyi.project.business.monthschedule.request.AiAssignRequest;
import com.ruoyi.project.business.monthschedule.request.MonthScheduleSaveOrUpdateRequest;
import com.ruoyi.project.business.monthschedule.response.MonthScheduleResponse;
import com.ruoyi.project.business.monthschedule.service.IMonthScheduleService;
import com.ruoyi.project.business.schedefine.request.ListAllScheduleDefineRequest;
import com.ruoyi.project.business.schedefine.response.ScheduleDefineResponse;
import com.ruoyi.project.business.schedefine.service.IScheduleDefineService;
import com.ruoyi.project.business.schedulerule.entity.MonthScheduleRule;
import com.ruoyi.project.business.schedulerule.service.IMonthScheduleRuleService;
import com.ruoyi.project.business.utils.BusinessUtil;
import com.ruoyi.project.system.entity.SysUser;
import com.ruoyi.project.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 月排班Service业务层处理
 * 
 * @author ${author}
 * @date 2024-02-05
 */
@Slf4j
@Service
public class MonthScheduleServiceImpl implements IMonthScheduleService {
    @Autowired
    private MonthScheduleMapper monthScheduleMapper;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private IMonthScheduleRuleService scheduleRuleService;
    @Autowired
    private IScheduleDefineService scheduleDefineService;
    @Autowired
    private IAskLeaveService askLeaveService;

    /**
     * 查询月排班
     * 
     * @param id 月排班ID
     * @return 月排班
     */
    @Override
    public MonthSchedule selectMonthScheduleById(Long id) {
        return monthScheduleMapper.selectMonthScheduleById(id);
    }

    /**
     * 查询月排班列表（适用于数据管理页面）
     *
     * @param param 月排班
     * @return 月排班
     */
    @Override
    public List<MonthSchedule> selectMonthScheduleList(MonthSchedule param, Long tenantId) {
        param.setTenantId(tenantId);
        return monthScheduleMapper.selectMonthScheduleList(param);
    }

    /**
     * 查询月排班列表  适用于功能页面
     *
     * @return 月排班
     */
    @Override
    public List<MonthScheduleResponse> selectMonthScheduleListWithUser(Date yearMonth, Long operatorId, Long deptId, Long tenantId) {
        Integer year = DateUtils.year(yearMonth);
        Integer month = DateUtils.month(yearMonth);
        return monthScheduleMapper.selectMonthScheduleListWithUser(year, month, operatorId, deptId, tenantId);
    }

    /**
     * 新增月排班
     * 
     * @param request 月排班
     * @return 结果
     */
    @Override
    public Long saveOrUpdate(MonthScheduleSaveOrUpdateRequest request, Long operatorId, Long tenantId) {
        Date yearMonth = request.getYearMonth();
        Integer year = DateUtils.year(yearMonth);
        Integer month = DateUtils.month(yearMonth);
        Long userId = request.getUserId();


        Long id = monthScheduleMapper.findExistedId(userId, year, month, tenantId);
        MonthSchedule monthSchedule = BeanUtils.copy(request, MonthSchedule.class);
        monthSchedule.setYear(year);
        monthSchedule.setMonth(month);

        // 判断有没有请假
        List<Integer> aimDayOfMonths = getAimDayOfMonths(monthSchedule);
        if(CollectionUtils.isNotEmpty(aimDayOfMonths)) {
            // 获取所有排班规则
            PageClickAssignStrategy strategy = new PageClickAssignStrategy();

            strategy.setAimDayOfMonths(aimDayOfMonths);
            strategy.setUserId(userId);
            strategy.setTenantId(tenantId);
            strategy.setYear(year);
            strategy.setMonth(month);
            strategy.check(); // TODO


            if(id == null) {
                id = this.insertMonthSchedule(monthSchedule, operatorId, tenantId);
            } else {
                monthSchedule.setId(id);
                this.updateMonthSchedule(monthSchedule, operatorId, tenantId);
            }
        }



        return id;
    }

    private static List<Integer> getAimDayOfMonths(MonthSchedule monthSchedule) {
        List<Integer> aimDayOfMonths = new ArrayList<>();
        for (int i = 1; i <= 31 ; i++) {
            Object value = BusinessUtil.getFieldValueByName(monthSchedule, i);
            if(value != null) {
                // 有没有请假
                aimDayOfMonths.add(i);
            }
        }
        return aimDayOfMonths;
    }

    /**
     * 新增月排班
     *
     * @param monthSchedule 月排班
     * @return 结果
     */
    @Override
    public Long insertMonthSchedule(MonthSchedule monthSchedule, Long operatorId, Long tenantId) {
        monthSchedule.setCreateTime(DateUtils.getNowDate());
        monthSchedule.setTenantId(tenantId);
        monthSchedule.setCreator(operatorId);
        return monthScheduleMapper.insertMonthSchedule(monthSchedule);
    }


    /**
     * 修改月排班
     * 
     * @param monthSchedule 月排班
     * @return 结果
     */
    @Override
    public int updateMonthSchedule(MonthSchedule monthSchedule, Long operatorId, Long tenantId) {
        monthSchedule.setUpdateTime(DateUtils.getNowDate());
        monthSchedule.setTenantId(tenantId);
        monthSchedule.setUpdater(operatorId);
        return monthScheduleMapper.updateMonthSchedule(monthSchedule);
    }

    /**
     * 批量删除月排班
     * 
     * @param ids 需要删除的月排班ID
     * @return 结果
     */
    @Override
    public int deleteMonthScheduleByIds(Long[] ids) {
        return monthScheduleMapper.deleteMonthScheduleByIds(ids);
    }

    /**
     * 删除月排班信息
     * 
     * @param id 月排班ID
     * @return 结果
     */
    @Override
    public int deleteMonthScheduleById(Long id) {
        return monthScheduleMapper.deleteMonthScheduleById(id);
    }

    @Override
    public List<EmpDailySchedule> aiAssign(AiAssignRequest request, Long operatorId, Long tenantId) {
        Date yearMonth = request.getYearMonth();
        List<Long> userIds = request.getUserIds();
        Long deptId = request.getDeptId();

        List<SysUser> sysUsers = null;
        if(CollectionUtils.isEmpty(userIds)) {
            sysUsers = userService.findUsersOfDeptId(deptId, tenantId);
        } else {
            sysUsers = userService.selectUserListByIds(userIds, tenantId);
        }
        if(CollectionUtils.isNotEmpty(sysUsers)) {
            int year = DateUtils.year(yearMonth);
            int month = DateUtils.month(yearMonth);
            List<MonthScheduleEmp> emps = initEmpAndEmpRules(tenantId, userIds, year, month, sysUsers);


            ListAllScheduleDefineRequest queryScheduleDefineRequest = new ListAllScheduleDefineRequest();
            queryScheduleDefineRequest.setDeptId(deptId);
            List<ScheduleDefineResponse> scheduleDefineResponses = scheduleDefineService.selectAllOfDeptId(queryScheduleDefineRequest, tenantId);
            List<ScheduleDefineMirror> scheduleDefineMirrors = BeanUtils.copyList(scheduleDefineResponses, ScheduleDefineMirror.class);

            CommonStrategy commonStrategy = new CommonStrategy();

            List<ShiftDefine> shiftDefines = DateUtils.joinDateAndScheduleDefine(year, month);
            MonthScheduleSolution solution = commonStrategy.doScheduleAssign(emps, shiftDefines, scheduleDefineMirrors);
            log.debug("智能排班结果 {}", JSON.toJSONString(solution));
            // 入库
            save(solution.getAllShifts(), year, month, operatorId, tenantId);
            return solution.getAllShifts();
        }
        return new ArrayList<>();
    }

    @Override
    public void checkSchedule(Long userId, Integer year, Integer month, int dayOfMonth, Long tenantId) {
        // 实际上应该只有一条
        List<MonthScheduleResponse> monthScheduleResponses = monthScheduleMapper.selectMonthScheduleListWithUser(year, month, userId, null, tenantId);
        if(CollectionUtils.isEmpty(monthScheduleResponses)) {
            throw new CustomException(month + "月" + dayOfMonth + "日 未排班, 不能打卡", -1);
        }
        MonthScheduleResponse monthScheduleResponse = monthScheduleResponses.get(0);
        Object value = BusinessUtil.getFieldValueByName(monthScheduleResponse, dayOfMonth);
        if(value == null) {
            throw new CustomException(month + "月" + dayOfMonth + "日 未排班, 不能打卡", -1);
        }
    }

    private void save(List<EmpDailySchedule> allShifts, int year, int month, Long operatorId, Long tenantId) {
        Date now = DateUtils.getNowDate();
        if (CollectionUtils.isNotEmpty(allShifts)) {
            Map<Long, List<EmpDailySchedule>> collect = allShifts.stream().collect(Collectors.groupingBy(EmpDailySchedule::getUserId));

            Set<Map.Entry<Long, List<EmpDailySchedule>>> entries = collect.entrySet();
            List<MonthSchedule> schedulesOfAllUsers = entries.stream().map(entry -> {
                Long userId = entry.getKey();
                List<EmpDailySchedule> shifts = entry.getValue();
                MonthSchedule monthSchedule = transferShiftToEntity(userId, year, month, shifts, operatorId, tenantId, now);
                return monthSchedule;
            }).collect(Collectors.toList());

            List<Long> userIds = new ArrayList<>(collect.keySet());
            monthScheduleMapper.deleteMonthSchedules(userIds, year, month, tenantId);

            batchSave(schedulesOfAllUsers);
        }

    }

    private void batchSave(List<MonthSchedule> schedulesOfUser) {
        if (CollectionUtils.isNotEmpty(schedulesOfUser)) {
            monthScheduleMapper.batchInsert(schedulesOfUser);
        }
    }

    private MonthSchedule transferShiftToEntity(Long userId, int year, int month,
                                                List<EmpDailySchedule> shifts, Long operatorId, Long tenantId, Date now) {
        MonthSchedule monthSchedule = new MonthSchedule();
        monthSchedule.setCreator(operatorId);
        monthSchedule.setCreateTime(now);
        monthSchedule.setUpdater(operatorId);
        monthSchedule.setUpdateTime(now);
        monthSchedule.setDeleted(Boolean.FALSE);
        monthSchedule.setTenantId(tenantId);
        monthSchedule.setUserId(userId);
        monthSchedule.setYear(year);
        monthSchedule.setMonth(month);

        shifts.forEach(shift -> {
            int dayOfMonth = shift.getDayOfMonth();
            long value = shift.getScheduleDefine().getId().longValue();
            String fieldName = "dayOfMonth" + dayOfMonth;
            // 获取 MonthSchedule 类的 dayOfMonth 字段
            BusinessUtil.setFieldName(monthSchedule, fieldName, value);
        });

        log.info("monthSchedule to save {} ", JSON.toJSONString(monthSchedule));

        return monthSchedule;

    }


    /**
     * 解决管理者的需求。
     * @return
     */
    private List<Rule> getSolutionRules() {

        EmpCountPerDay empCountPerDay = (EmpCountPerDay)RuleUtil.initializeMonthRuleBean(EmpCountPerDay.class.getSimpleName(), 1, "每天能排的总人数", false);
        empCountPerDay.setMinCountPerDay(1);
        empCountPerDay.setMaxCountPerDay(2);

        // TODO 这个也要做到页面上
//        SchedefineWish schedefineWish = (SchedefineWish)RuleUtil.initializeMonthRuleBean(SchedefineWish.class.getSimpleName(), 1, "员工对班次的期望", false);


        List<Rule> solutionRules = new ArrayList<>();
        solutionRules.add(empCountPerDay);
        return solutionRules;
    }

    /**
     * 员工初始化、
     * 员工规则初始化 （仅解决员工的需求）
     * @param tenantId
     * @param userIds
     * @param year
     * @param month
     * @param sysUsers
     * @return
     */
    private List<MonthScheduleEmp> initEmpAndEmpRules(Long tenantId, List<Long> userIds, int year, int month, List<SysUser> sysUsers) {
        List<MonthScheduleRule> dbRules = scheduleRuleService.selectRulesByUserIds(userIds, year, month, tenantId);
        Map<Long, List<Rule>> rulesMap;
        if(CollectionUtils.isNotEmpty(dbRules)) {
            rulesMap = dbRules.stream().map(rule -> {
                String clazzName = rule.getBeanname();
                Integer order = 0; //  TODO 回头设计到页面上去
                String breakTips = "";
                Boolean breakIfReject = false; // TODO 回头设计到页面上去
                Rule tmp = RuleUtil.initializeMonthRuleBean(clazzName, order, breakTips, breakIfReject);
                tmp.setUserId(rule.getUserId());
                return tmp;
            }).collect(Collectors.groupingBy(Rule::getUserId));
        } else {
            rulesMap = new HashMap<>();
        }

        // 还有要个全局规则，用于控制管理者的需求。就完美了
        List<Rule> globalRules = getSolutionRules(); // TODO
        List<MonthScheduleEmp> emps = sysUsers.stream().map(user -> {
            MonthScheduleEmp emp = new MonthScheduleEmp();
            emp.setUserId(user.getUserId());
            List<Rule> rules = rulesMap.getOrDefault(user.getUserId(), new ArrayList<>());
            HashSet<Rule> tmpSet = new HashSet<>(rules);
            if(CollectionUtils.isNotEmpty(globalRules)) {
                tmpSet.addAll(globalRules);
            }
            emp.setRules(new ArrayList<>(tmpSet));
            return emp;
        }).collect(Collectors.toList());
        return emps;
    }
}
